﻿using System;
using System.Collections.Specialized;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Runtime.Caching;
using System.Runtime.InteropServices;
using System.Text;
using System.Windows.Forms;
using Chromium;
using Chromium.WebBrowser;
using Kugar.Core.ExtMethod;
using Kugar.UI.RazorUI.RazorEngine;
using NetDimension.NanUI;

namespace Kugar.UI.RazorUI.CefEngine
{


    /// <summary>
    /// 参考NanUI的Handler,,修改了对cshtml文件的处理
    /// </summary>
    class RazorResourceHandler : CfxResourceHandler
    {
        private int readResponseStreamOffset;
        private string requestUrl = null;
        private readonly string basePath;

        private WebResource webResource;
        private BrowserCore browser;

        private CfxFrame _frame = default;
        private CfxRequest _request = default;

        private static MemoryCache _cache=new MemoryCache("test"); 


        private string physicalPath = null;

        private GCHandle gcHandle;

        private string domain = null;

        private int? buffStartPostition = null;
        private int? buffEndPostition = null;
        private bool isPartContent = false;


        internal RazorResourceHandler(string assertPath, BrowserCore browser, CfxFrame frame, string domain)
        {
            gcHandle = GCHandle.Alloc(this);
            this.domain = domain;
            this.browser = browser;
            this.basePath = assertPath;
            this.GetResponseHeaders += OnGetResponseHeaders;
            this.ProcessRequest += OnProcessRequest;
            this.ReadResponse += OnReadResponse;
            this.CanGetCookie += (_, e) => e.SetReturnValue(false);
            this.CanSetCookie += (_, e) => e.SetReturnValue(false);

            _frame = frame;
        }

        private void OnProcessRequest(object sender, Chromium.Event.CfxProcessRequestEventArgs e)
        {
            _request = e.Request;

            var request = e.Request;
            var callback = e.Callback;

            var uri = new Uri(request.Url);
            requestUrl = request.Url;

            var headers = request.GetHeaderMap().Select(x => new { Key = x[0], Value = x[1] }).ToList();

            var contentRange = headers.FirstOrDefault(x => x.Key.ToLower() == "range");

            if (contentRange != null)
            {
                var group = System.Text.RegularExpressions.Regex.Match(contentRange.Value, @"(?<start>\d+)-(?<end>\d*)")?.Groups;
                if (group.HasData())
                {
                    if (!string.IsNullOrEmpty(group["start"].Value) && int.TryParse(group["start"].Value, out int startPos))
                    {
                        buffStartPostition = startPos;
                    }

                    if (!string.IsNullOrEmpty(group["end"].Value) && int.TryParse(group["end"].Value, out int endPos))
                    {
                        buffEndPostition = endPos;
                    }
                }

                isPartContent = true;

            }

            readResponseStreamOffset = 0;

            if (buffStartPostition.HasValue)
            {
                readResponseStreamOffset = buffStartPostition.Value;
            }

            var fileName = string.IsNullOrEmpty(domain) ? $"{uri.Authority}{Uri.UnescapeDataString(uri.AbsolutePath)}" : Uri.UnescapeDataString(uri.AbsolutePath);

            if (fileName.StartsWith("/") && fileName.Length > 1)
            {
                fileName = fileName.Substring(1);
            }

            var endTrimIndex = fileName.LastIndexOf('/');

            if (endTrimIndex > -1)
            {
                var tmp = fileName.Substring(0, endTrimIndex);
                tmp = tmp.Replace("-", "_");

                fileName = string.Format("{0}{1}", tmp, fileName.Substring(endTrimIndex));
            }

            physicalPath = System.IO.Path.Combine(basePath, fileName);

            var fileInfo = new System.IO.FileInfo(physicalPath);

            

            if (RazorResourceLoader.ExistsHandler(fileName))
            {
                //TODO:需要加入错误处理,以及返回500页面

                if (request.ResourceType==CfxResourceType.SubResource)
                {

                    callback.Continue();
                    e.SetReturnValue(true);
                    return;
                }

                Debug.WriteLine("加载:" + requestUrl);

                using (var buff = new MemoryStream())  //此处判断是否为cshtml结尾,如果是,,调用处理引擎处理
                using (var writer = new StreamWriter(buff, Encoding.UTF8))
                {
                    var handler = RazorResourceLoader.FindHandler(fileName);

                    if (handler == null)
                    {
                        callback.Continue();
                        e.SetReturnValue(true);
                        return;
                    }

                    var httpRequest = new HttpRequest(request);


                    if (handler is CefRazorFormBase form )
                    {
                        foreach (RazorUIForm openForm in Application.OpenForms)
                        {
                            if (openForm == null)
                            {
                                continue;
                            }

                            if (openForm.Browser == browser.Browser)
                            {
                                openForm.SetCurrentHandleForm(_frame, form);
                                form.Browser = browser;
                                form.SourceForm = openForm;
                                form.CurrentFrame = _frame;

                                form.OnFormLoading();

                                break;
                            }
                        }
                    }

                    handler.RequestContext = httpRequest;

                    var result = handler.Execute();

                    if (result is RazorResult r)
                    {
                        r.ViewContext = new ViewContext(new HttpContext(httpRequest));
                    }

                    result.Render(writer);

                    buff.Position = 0;

                    var html = buff.ReadAllBytes();

                    webResource = new WebResource(html, "text/html");

                    writer.Flush();

                    callback.Continue();
                    e.SetReturnValue(true);
                }
            }
            else if (fileInfo.Extension == ".cshtml")
            {
                if (File.Exists(physicalPath))
                {
                    var result = new RazorResult(fileName, null);

                    using (var buff = new MemoryStream())  //此处判断是否为cshtml结尾,如果是,,调用处理引擎处理
                    using (var writer = new StreamWriter(buff, Encoding.UTF8))
                    {
                        result.Render(writer);

                        buff.Position = 0;

                        var html = buff.ReadAllBytes();

                        webResource = new WebResource(html, "text/html");

                        writer.Flush();
                    }

                    callback.Continue();
                    e.SetReturnValue(true);
                }
                else
                {
                    e.SetReturnValue(false);
                }

            }
            else if (System.IO.File.Exists(physicalPath))
            {
                var buff = System.IO.File.ReadAllBytes(physicalPath);

                webResource = new WebResource(buff, CfxRuntime.GetMimeType(System.IO.Path.GetExtension(fileName).TrimStart('.')));

                callback.Continue();
                e.SetReturnValue(true);
            }
            else
            {

                callback.Continue();
                e.SetReturnValue(false);
                Console.WriteLine($"[找不到资源]:\t{requestUrl}");

            }

        }

        private void OnGetResponseHeaders(object sender, Chromium.Event.CfxGetResponseHeadersEventArgs e)
        {
            if (_request.ResourceType== CfxResourceType.SubResource)
            {
                e.ResponseLength =0;
                e.Response.MimeType ="text/*";
                e.Response.Status = 200;
                e.Response.StatusText = "OK";
            }
            else
            {
                if (webResource == null)
                {
                    e.Response.Status = 404;
                }
                else
                {
                    var length = webResource.data.Length;
                    e.ResponseLength = webResource.data.Length;
                    e.Response.MimeType = webResource.mimeType;
                    e.Response.Status = 200;
                    e.Response.StatusText = "OK";

                    var headers = e.Response.GetHeaderMap();

                    if (isPartContent)
                    {
                        headers.Add(new string[2] { "Accept-Ranges", "bytes" });
                        var startPos = 0;
                        var endPos = length - 1;



                        if (buffStartPostition.HasValue && buffEndPostition.HasValue)
                        {
                            startPos = buffStartPostition.Value;
                            endPos = buffEndPostition.Value;
                        }
                        else if (!buffEndPostition.HasValue && buffStartPostition.HasValue)
                        {
                            startPos = buffStartPostition.Value;
                        }



                        headers.Add(new string[] { "Content-Range", $"bytes {startPos}-{endPos}/{webResource.data.Length}" });
                        headers.Add(new string[] { "Content-Length", $"{endPos - startPos + 1}" });

                        e.ResponseLength = (endPos - startPos + 1);

                        e.Response.Status = 206;

                    }


                    e.Response.SetHeaderMap(headers);

                }                
            }

        }

        private void OnReadResponse(object sender, Chromium.Event.CfxReadResponseEventArgs e)
        {
            

            int bytesToCopy = webResource.data.Length - readResponseStreamOffset;

            if (bytesToCopy > e.BytesToRead)
                bytesToCopy = e.BytesToRead;

            Marshal.Copy(webResource.data, readResponseStreamOffset, e.DataOut, bytesToCopy);

            e.BytesRead = bytesToCopy;
            
            readResponseStreamOffset += bytesToCopy;

            Debug.WriteLine( $"e.byteToCopy:{ e.BytesToRead};request:{requestUrl},readResponseStreamOffset:{readResponseStreamOffset}");

            e.SetReturnValue(true);


            if (readResponseStreamOffset == webResource.data.Length)
            {
                gcHandle.Free();
                Console.WriteLine($"[加载文件资源]:\t{requestUrl}");
                //Console.WriteLine($" -> \t{physicalPath}");
            }

            //e.Callback.Continue();

        }

    }
}
